<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
</body>
</html>
<script>
class EventBase {
  constructor() {
    this._default = 'default'
    this._slice = Array.prototype.slice
    this._shift = Array.prototype.shift
    this._unshift = Array.prototype.unshift
    this.namespaceCache = {}
  }
  /**
   * 迭代执行回调
   * @param arr key对应的单个缓存栈
   * @param fn 可执行的回调函数
   * @return ret 
  */
  each(arr, fn) {
    let ret
    for(let i = 0, l = arr.length; i < l; i++) {
      ret = fn.call(arr[i], i, arr[i])
    }
    return ret
  }
  /**
   * 消息订阅
   * @param key 消息标识
   * @param fn 消息订阅时的回调
   * @param cache 消息列表
  */
  _listen(key, fn, cache) {
    if(!cache[key]) {
      cache[key] = []
    }
    cache[key].push(fn)
    console.log('cache', cache);
  }
  /**
   * 移除消息订阅
   * @param key 消息标识
   * @param cache 消息列表
   * @param fn 消息订阅时的回调
  */
  _remove(key, cache, fn) {
    let fns = cache[key]
    if(!fns) return false
    if(!fn) {
      fns = []
    } else {
      for(let i = fns.length - 1; i >= 0; i--) {
        if(fns[i] === fn) {
          fns.splice(i, 1)
        }
      }
    }
  }
  /**
   * 消息发布
   * @param cache 方法名
   * @param key 标识
  */
  _trigger() {
    let cache = this._shift.call(arguments),
      key = this._shift.call(arguments),
      stack = cache[key],
      args = arguments,
      _self = this
    if(!stack || !stack.length) return
    return _self.each(stack, function() {
      return this.apply(_self, args)
    })
  }
  /**
   * 创建命名空间
   * @param namespace 命名空间
  */
  _create(namespace) {
    namespace = namespace || this._default
    let _self = this
    let cache = {},
      offlineStack = [], // 离线事件
      ret = {
        // 消息订阅
        listen(key, fn, last) {
          _self._listen(key, fn, cache)
          if(offlineStack === null) return
          if(last === 'last') {
            offlineStack.length && offlineStack.pop()()
          } else {
            _self.each(offlineStack, function() {
              // this () =>  _self._trigger.apply(_self, args)
              this()
            })
          }
          offlineStack = null
        },
        // 订阅一次性消息
        one(key, fn, last) {
          _self._remove(key, cache)
          this.listen(key, fn, last)
        },
        // 移除消息订阅
        remove(key, fn) {
          _self._remove(key, cache, fn)
        },
        // 消息发布
        trigger() {
          let fn, args = arguments
          _self._unshift.call(arguments, cache)
          // arguments [cache, key, ...args]
          fn = () =>  _self._trigger.apply(_self, args)
          if(offlineStack) {
            return offlineStack.push(fn)
          }
          return fn()
        }
      }
    return namespace ?
      (this.namespaceCache[namespace] ?
        this.namespaceCache[namespace]
          : this.namespaceCache[namespace] = ret)
            : ret
  }
}

class Event extends EventBase {
  constructor() {
    super()
    this.create = this._create
  }
  one(key, fn, last) {
    let event = this.create()
    event.one(key, fn, last)
  }
  remove(key, fn) {
    let event = this.create()
    event.remove(key, fn)
  }
  listen(key, fn, last) {
    let event = this.create()
    event.listen(key, fn, last)
  }
  trigger() {
    let event = this.create()
    event.trigger.apply(this, arguments)
  }
}

let e = new Event()
e.trigger('click', 1)
e.trigger('click', 2)
e.listen('click', a => {
  console.log(a)
})
// e.create('namespace1').listen('click', a => {
//   console.log('namespace1', a)
// })
// e.create('namespace1').trigger('click', 2)
// e.create('namespace2').listen('click', a => {
//   console.log('namespace2', a)
// })
// e.create('namespace2').trigger('click', 3)
</script>